package com.df.datacloud.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

/**
 * <p>
 * http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js
 * </p>
 */
public class RSAUtils {
	
	/**
	 */
	public static final String KEY_ALGORITHM = "RSA";
	
	/**
	 */
	public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
	
	/**
	 */
	private static final String PUBLIC_KEY = "RSAPublicKey";
	
	/**
	 */
	private static final String PRIVATE_KEY = "RSAPrivateKey";
	
	private static final String publicKey ="";
	
	private static Logger logger = LoggerFactory.getLogger(RSAUtils.class);
	
	/**
	 * @return
	 * @throws Exception
	 */
	public static Map<String, Object> genKeyPair() throws Exception {
		KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
		keyPairGen.initialize(1024);
		// RSAKeyGenParameterSpec spec = new RSAKeyGenParameterSpec(1024, RSAKeyGenParameterSpec.F0);
		// keyPairGen.initialize(spec);
		KeyPair keyPair = keyPairGen.generateKeyPair();
		RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
		RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
		Map<String, Object> keyMap = new HashMap<String, Object>(2);
		keyMap.put(PUBLIC_KEY, publicKey);
		keyMap.put(PRIVATE_KEY, privateKey);
		return keyMap;
	}
	
	public static String sign(byte[] data, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
		signature.initSign(privateK);
		signature.update(data);
		return Base64Utils.encode(signature.sign()).trim();
	}
	
	public static boolean verify(byte[] data, String publicKey, String sign)  {
		try {
			byte[] keyBytes = Base64Utils.decode(publicKey);
			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
			KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
			PublicKey publicK = keyFactory.generatePublic(keySpec);
			Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
			signature.initVerify(publicK);
			signature.update(data);
			return signature.verify(Base64Utils.decode(sign));
		}catch (Exception e){
			logger.error("验签失败，e={}",e);
		}
		return false;
	}
	
	/**
	 * * 加密 *
	 *
	 * @param data 待加密的明文数据 *
	 * @return 加密后的数据 *
	 * @throws Exception
	 */
	public static byte[] encryptByPublicKey(byte[] data, String publicKey) {
		byte[] keyBytes = Base64Utils.decode(publicKey);
		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
		KeyFactory keyFactory ;
		Key publicK;
		try {
			keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
			publicK = keyFactory.generatePublic(x509KeySpec);
			Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(Cipher.ENCRYPT_MODE, publicK);
			// 获得加密块大小，如：加密前数据为128个byte，而key_size=1024
			int blockSize = cipher.getBlockSize();
			// 加密块大小为127
			// byte,加密后为128个byte;因此共有2个加密块，第一个127
			// byte第二个为1个byte
			int outputSize = cipher.getOutputSize(data.length);// 获得加密块加密后块大小
			int leavedSize = data.length % blockSize;
			int blocksSize = leavedSize != 0 ? data.length / blockSize + 1 : data.length / blockSize;
			byte[] raw = new byte[outputSize * blocksSize];
			int i = 0;
			while (data.length - i * blockSize > 0) {
				if (data.length - i * blockSize > blockSize) {
					cipher.doFinal(data, i * blockSize, blockSize, raw, i * outputSize);
				} else {
					cipher.doFinal(data, i * blockSize, data.length - i * blockSize, raw, i * outputSize);
				}
				i++;
			}
			return raw;
		} catch (Exception e) {
			logger.error("加密数据失败，e={}",e);
		}
		return null;
	}
	
	/**
	 * * 解密
	 *
	 * @param raw 已经加密的数据 *
	 * @return 解密后的明文 *
	 * @throws Exception
	 */
	@SuppressWarnings("static-access")
	public static byte[] decryptByPrivateKey(byte[] raw, String privateKey) throws Exception {
		byte[] keyBytes = Base64Utils.decode(privateKey);
		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
		KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
		PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
		try {
			Cipher cipher = Cipher.getInstance("RSA", new org.bouncycastle.jce.provider.BouncyCastleProvider());
			cipher.init(cipher.DECRYPT_MODE, privateK);
			int blockSize = cipher.getBlockSize();
			ByteArrayOutputStream bout = new ByteArrayOutputStream(64);
			int j = 0;
			while (raw.length - j * blockSize > 0) {
				bout.write(cipher.doFinal(raw, j * blockSize, blockSize));
				j++;
			}
			
			return bout.toByteArray();
		} catch (Exception e) {
			throw new Exception(e.getMessage(), e);
		}
	}
	
	public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PRIVATE_KEY);
		return Base64Utils.encode(key.getEncoded());
	}
	
	public static String getPublicKey(Map<String, Object> keyMap) throws Exception {
		Key key = (Key) keyMap.get(PUBLIC_KEY);
		return Base64Utils.encode(key.getEncoded());
	}
	

	
	
    private static void testWrite(Map<String,String> map) throws IOException {
		Properties p = new Properties();
		Iterator<Map.Entry<String, String>> entries = map.entrySet().iterator();
		while (entries.hasNext()) {
			Map.Entry<String, String> entry = entries.next();
			p.setProperty(entry.getKey(), entry.getValue());
		}
		File file = new File("/Users/lijun/dev_space/springcloud-simple/platform-gateway/src/main/resources/config.properties");
		if(!file.exists()) {
			file.createNewFile();
		}
		FileOutputStream out = new FileOutputStream(file);
		p.store(out, "Create by Shawearn!");
		out.close();
	}
	
	
	
	public static  Boolean verification(String body,String signStr) {
		byte[] contentBytes = RSAUtils.encryptByPublicKey(body.getBytes(), publicKey);
		Boolean flag =verify(contentBytes,publicKey,signStr);
		return flag;
	}
	
	public static void main(String[] args) throws Exception {
		
		Map<String, Object> stringObjectMap = RSAUtils.genKeyPair();
		String publicKey = RSAUtils.getPublicKey(stringObjectMap);
		String privateKey = RSAUtils.getPrivateKey(stringObjectMap);
		
		Map<String,String> map = new HashMap<String, String>();
		map.put("publickey",publicKey);
		map.put("privateKey",privateKey);
		System.out.println("-----publickey---->" + publicKey);
		System.out.println("-----privateKey---->" + privateKey);
		testWrite(map);
		byte[] bytes1 = RSAUtils.encryptByPublicKey("test-lijun-李俊".getBytes(), publicKey);
		byte[] bytes2 = RSAUtils.decryptByPrivateKey(bytes1, privateKey);
		System.out.println("-----加密字符串解密---->" + new String(bytes2));
		System.out.println("pub-encode: " + Base64Utils.encode(bytes1));
		System.out.println("-----加密字符串长度----->" + bytes1.length);
		String signStr = sign(bytes1,privateKey);
		System.out.println("-----加密字符串的的签名---->  sign=" + signStr);
		Boolean flag =verify(bytes1,publicKey,signStr);
		System.out.println("-----加密串的验签结果---->  sign=  " + flag );
	}
	
}

